/**
	Autora: Rose Yuri Shimizu
**/

#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h> 
#include <sys/dir.h>
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>

#define SIZE_Q 20
#define MAX_SDIR 200
typedef int semaphore;

// Fila de arquivos e diretorios gerado pelo produtorA e consumido pelo consumidorA
//struct file * buffer_arquivos;
//struct directory * buffer_diretorios;

struct nodo{
	char path[MAX_SDIR];
	struct nodo *prox;
};
typedef struct nodo *nodoptr;

typedef struct {
	nodoptr head, tail;
	int size;
	semaphore full, empty;
	pthread_mutex_t *mut;	
	pthread_cond_t *notFull, *notEmpty;
} queue;

queue *fifoD;
queue *fifoF;

char *to_base_path;
char *from_base_path;

// Numero threads consumidores e produtores
int n_producerD = 1;
int n_consumerF = 2;

void *producer ();
void *consumer ();

queue *queueInit (void);
nodoptr getnodo();
void queueDelete (queue *q);
void queueAdd (queue *q,char in[]);
void queueDel (queue *q);



void percorre_diretorios(){
	struct dirent **lista_dir;
	struct stat finfo;
	char head[MAX_SDIR];
	char dir[MAX_SDIR];
	char path_dir[MAX_SDIR];
	char path_dir_in[MAX_SDIR];
	int i,n;

	strcpy(head,fifoD->head->path);
	queueDel(fifoD);

	memset(path_dir, 0, sizeof(path_dir_in));
	strcat(path_dir,to_base_path);
	strcat(path_dir,head);
	strcat(path_dir,"/");
	strcat(head,"/");

	i = scandir(path_dir,&lista_dir,0,0);

//	printf("Dir: %s\n",path_dir);

	if(i > 0){
		for(n=0;n<i;n++){

			if(strcmp(lista_dir[n]->d_name,".")==0 ||
				strcmp(lista_dir[n]->d_name,"..")==0){
				continue;
			}

			memset(dir, 0, sizeof(dir));
			memset(path_dir_in, 0, sizeof(path_dir_in));
			strcat(path_dir_in,path_dir);
			strcat(path_dir_in,lista_dir[n]->d_name);

			strcat(dir,head);
			strcat(dir,lista_dir[n]->d_name);

			lstat(path_dir_in, &finfo);

			if((long long)finfo.st_size > 0){					//se tiver conteudo
				if(S_ISDIR(finfo.st_mode)){					// se diretorio 
					queueAdd (fifoD,dir);				// adicionar à lista
				}else {

					pthread_mutex_lock (fifoF->mut);
					while (fifoF->full) {
						printf ("producer FILE: queue FULL.\n");
						pthread_cond_wait (fifoF->notFull, fifoF->mut);
					}
	
					queueAdd (fifoF,dir);
					fifoF->size++;
					if(fifoF->size > 3) fifoF->full=1;

					pthread_mutex_unlock (fifoF->mut);
					pthread_cond_signal (fifoF->notEmpty);
					usleep (100000);
				}
			}
		}

	}
	return;
}


v10
int main (int argc, char *argv[])
{

	int i;

	pthread_t prod[n_producerD], cons[n_consumerF];

	fifoD = queueInit ();
	fifoF = queueInit ();


	to_base_path = argv[1];
	from_base_path = argv[2];

	queueAdd (fifoD, "");

	if (fifoD ==  NULL) {
		fprintf (stderr, "main: Queue Init failed.\n");
		exit (1);
	}


	for (i = 0; i < n_producerD; i++) { 
		pthread_create (&prod[i], NULL, producer,NULL);
	}

	for (i = 0; i < n_consumerF; i++) {
		pthread_create (&cons[i], NULL, consumer,NULL);
	}

	for (i = 0; i < n_producerD; i++) { 
		pthread_join (prod[i], NULL);
	}

	for (i = 0; i < n_consumerF; i++) {
		pthread_join (cons[i], NULL);
	}

	queueDelete (fifoD);
	queueDelete (fifoF);

	return 0;
}

void *producer ()
{
	int i;
	while(1){ 
		pthread_mutex_lock (fifoD->mut);
		while (fifoD->full) {
			printf ("producer: queue FULL.\n");
			pthread_cond_wait (fifoD->notFull, fifoD->mut);
		}
		while (fifoD->empty) {
			printf ("producer: queue EMPTY.\n");
			pthread_cond_wait (fifoD->notEmpty, fifoD->mut);
		}
		percorre_diretorios();

		pthread_mutex_unlock (fifoD->mut);
		pthread_cond_signal (fifoD->notEmpty);
		usleep (10000);
	}

}
void copy_file(){

	int in, out, fd;
	char file[MAX_SDIR];
	char path_dir1[MAX_SDIR];
	char path_dir2[MAX_SDIR];
	char block[1024];
	char *result,s[MAX_SDIR];

	struct stat finfo;
	memset(s, 0, sizeof(s));
	memset(path_dir2, 0, sizeof(path_dir2));
	memset(path_dir1, 0, sizeof(path_dir1));

	strcpy(file, fifoF->head->path);
	queueDel(fifoF);

	strcat(path_dir1,to_base_path);
	strcat(path_dir1,file);

	strcat(path_dir2,from_base_path);
	strcat(path_dir2,file);

	in = open(path_dir1, O_RDONLY);
	out = open(path_dir2, O_WRONLY| O_CREAT, S_IRWXU | S_IRWXG);

	if(out < 0){ 
		result = strtok(file, "/" );
		strcat(s,from_base_path);
		while( result != NULL ) {
			strcat(s,"/");
			strcat(s,result);
			result = strtok( NULL, "/" );
			if(result != NULL){
				//printf("Origem: %s\n", s);
				//printf("Destino: %s\n", s);
				mkdir(s,S_IWRITE | S_IWGRP | S_IEXEC | S_IREAD | S_IRGRP);
			}
		}
		out = open(path_dir2, O_WRONLY | O_CREAT, S_IRWXU | S_IRWXG);
	}

	//printf("Origem: %s %d\n", path_dir1, in);
	//printf("Destino: %s %d\n", path_dir2, out);


	fd=read(in,block,sizeof(block));
	write(out,block,fd);

	close(in);
	close(out);
}

void *consumer ()
{
	int i, d;
	while(1){
		pthread_mutex_lock (fifoF->mut);
		while (fifoF->empty) {
			printf ("consumer: queue EMPTY.\n");
			pthread_cond_wait (fifoF->notEmpty, fifoF->mut);
		}

		copy_file();	
//		queueDel(fifoF);
		pthread_mutex_unlock (fifoF->mut);
		pthread_cond_signal (fifoF->notFull);
		usleep (10000);
	}
}


nodoptr getnodo(){
	nodoptr p;
	p = (nodoptr) malloc(sizeof(struct nodo));
	return p;
}

queue *queueInit (void)
{
	queue *q;

	q = (queue *)malloc (sizeof (queue));
	if (q == NULL) return (NULL);
	q->size = 0;
	q->empty = 1;
	q->full = 0;
	q->head = NULL;
	q->tail = NULL;
	q->mut = (pthread_mutex_t *) malloc (sizeof (pthread_mutex_t));
	pthread_mutex_init (q->mut, NULL);
	q->notFull = (pthread_cond_t *) malloc (sizeof (pthread_cond_t));
	pthread_cond_init (q->notFull, NULL);
	q->notEmpty = (pthread_cond_t *) malloc (sizeof (pthread_cond_t));
	pthread_cond_init (q->notEmpty, NULL);
	
	return (q);
}

void queueDelete (queue *q)
{
	pthread_mutex_destroy (q->mut);
	free (q->mut);	
	pthread_cond_destroy (q->notFull);
	free (q->notFull);
	pthread_cond_destroy (q->notEmpty);
	free (q->notEmpty);
	free (q);
}

void queueAdd (queue *q, char in[])
{

	nodoptr n;
	n = getnodo();
	strcpy(n->path,in);

	n->prox = NULL;

	if (q->tail != NULL)
		q->tail->prox = n;
	q->tail = n;
	q->empty = 0;
	if (q->head == NULL)
		q->head = n;

	return;
}
	
void queueDel (queue *q)
{
	nodoptr n;

	if (q->head == NULL)
		exit;

	n = q->head;
	q->head = n->prox;
	if (q->head == NULL){
		q->tail = NULL;
		q->empty = 1;
	}
	q->full = 0;	
	q->size--;	
	free(n);
	return;
}


